using DA.AnimGraph;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;

namespace DA.AnimGraph.Editor {
    public static class AGAPublisher {

        internal class PublishContext {
            // in
            public AGA_Graph graph;
            public AGA_SubGraph subGraph;

            public AGA_Blackboard blackboard;

            // out
            public ResourcesCollector resourcesCollector;

            // debug
            public AUID currentNodeUuid;
            //public AUID currentSubGraphUuid;
        }

        internal class ResourcesCollector {
            public HashSet<AUID> resourcesUuid = new();

            public void Add(AUID uuid) {
                resourcesUuid.Add(uuid);
            }
        }

        public delegate void ExportUrlDelegate(AGA_Resources.Entry entry, out string url, out string url2);
        public static ExportUrlDelegate ToUrlFunc;

        internal static void ExportResourcesAsset(in AGA_Resources resources, ref AnimGraphResourcesReferenceAsset asset) {
            if (asset == null) {
                asset = ScriptableObject.CreateInstance<AnimGraphResourcesReferenceAsset>();
            }
            asset.resources = resources.entries.Select(x => new AnimGraphResourcesReferenceAsset.Entry() {
                uuid = x.uuid,
                resource = x.resource,
            }).ToList();
        }

        internal static AnimGraphPublishAsset Export(AGA_Root aga, in AGA_Resources resources, in AnimGraphPublishAsset.Timeline[] timelines) {
            AnimGraphPublishAsset publishAsset = new AnimGraphPublishAsset();

            PublishContext context = new PublishContext() {
                blackboard = aga.blackboard,
                resourcesCollector = new(),
            };

            publishAsset.graphs = new PublishGraph[aga.graphs.Count];
            for (int i = 0; i < aga.graphs.Count; i++) {
                var pGraph = ExportGraph(aga.graphs[i], context);
                publishAsset.graphs[i] = pGraph;
            }

            publishAsset.fileUuid = aga.uuid;
            publishAsset.resources = ExportResources(context.resourcesCollector, resources);
            publishAsset.timelines = timelines;

            return publishAsset;
        }

        static PublishGraph ExportGraph(AGA_Graph bGraph, PublishContext context) {
            var pGraph = new PublishGraph();
            pGraph.uuid = bGraph.uuid;
            pGraph.name = bGraph.name;

            int rootSubIdx = bGraph.FindeSubGraph(bGraph.rootSubGraph);
            var rootSub = bGraph.subGraphs[rootSubIdx];
            var outNodes = new List<PublishNode>();

            context.graph = bGraph;
            context.subGraph = rootSub;

            var index = ExportSubGraph(outNodes, context); // 返回值必然是0
            pGraph.nodes = outNodes.ToArray();
            //todo: blackboard
            return pGraph;
        }

        static int ExportSubGraph(List<PublishNode> outNodes, PublishContext context) {
            var bResultNode = context.subGraph.nodes[context.subGraph.FindNode(context.subGraph.rootNode)] as AGANode_Result;
            var bRootNode = context.subGraph.FindNextNodeInstance(bResultNode.uuid, bResultNode.result) as AGANode_FunctionBase;
            //pGraph.rootNode = ExportNode(bRootNode, bSubGraph, pGraph);
            var index = ExportNode(bRootNode, outNodes, context);

            return index;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="bNode">入口第一个node</param>
        /// <param name="bGraph">传入用于查询下一个subGraph，但是目前没有支持多sub</param>
        /// <param name="bSubGraph">传入用于查询下一个node</param>
        /// <param name="outNodes">导出完成的node添加进去</param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        internal static int ExportNode(AGANode_NodeBase bNode, List<PublishNode> outNodes, PublishContext context) {
            if (bNode is AGANode_FunctionBase fbNode) {
                context.currentNodeUuid = fbNode.uuid;
                var index = fbNode.Publish(outNodes, context);
                context.currentNodeUuid = default;
                return index;
            }
            // todo:支持多subGraph
            //else if (bNode is XXXNode) {
            //    var subIndex = bGraph.FindeSubGraph(uuid);
            //    var sub = bGraph.subGraphs[subIndex];
            //    return ExportSubGraph(bGraph, sub, pGraph);
            //}
            throw new NotImplementedException();
        }

        internal static AnimGraphPublishAsset.Resource[] ExportResources(ResourcesCollector collector, AGA_Resources res) {
            var urlList = new List<AnimGraphPublishAsset.Resource>();
            foreach (var resUuid in collector.resourcesUuid) {
                var entry = res.entries.Find(res => res.uuid == resUuid);
                try {
                    string url = null;
                    string url2 = null;
                    if (ToUrlFunc != null) {
                        ToUrlFunc(entry, out url, out url2);
                    } else {
                        ToUrlGR(entry, out url, out url2);
                    }
                    urlList.Add(new AnimGraphPublishAsset.Resource() {
                        uuid = resUuid,
                        url = url,
                        url2 = url2,
                    });
                } catch (Exception e) {
                    D.Exception($"导出资源报错: obj: {entry.resource},uuid: {entry.uuid}\n{e}");
                    throw;
                }
            }

            return urlList.ToArray();
        }

        static void ToUrlGR(AGA_Resources.Entry entry, out string url, out string url2) {
            var path = AssetDatabase.GetAssetPath(entry.resource);
            //url2 = obj.name;
            path = path.Substring("Assets/".Length);
            url = "asset://" + path;
            url2 = null;
        }
        static void ToUrl(AGA_Resources.Entry entry, out string url, out string url2) {
            url = AssetDatabase.GetAssetPath(entry.resource);
            url2 = entry.resource.name;
        }
    }
}
